package game.service;

import com.sghd.common.utils.ManagedException;
import com.sghd.common.utils.json.JsonUtils;
import data.model.BasePlayer;
import data.model.PropType;
import data.service.DataService;
import game.dic.Status;
import game.dic.TishiManager;
import game.model.ConfigRoom;
import game.model.InitDesk;
import network.SocketEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author fank
 * @date 2018-08-18 14:23
 * @desc
 **/
public class RoomService {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /** 桌子列表*/
    private ConcurrentMap<Integer, DeskService> desks = new ConcurrentHashMap<>();
    /** 房子配置*/
    private ConfigRoom configRoom;
    private DataService dataService;
    private GameService gameService;
    //等待加入玩家队列
    private BlockingQueue<PlayerService> waits = new LinkedBlockingQueue<>();
    /** 当前房间内，<压注档位，反分池>*/
    private ConcurrentMap<Integer, AtomicLong> refund = new ConcurrentHashMap<>();
    /** 抽水池*/
    private AtomicLong rebeat = new AtomicLong();

    public RoomService(ConfigRoom configRoom, DataService dataService, GameService gameService) {
        this.dataService = dataService;
        this.configRoom = configRoom;
        this.gameService = gameService;
        //初始化桌子
        for (int i = 0; i < configRoom.getDeskCount(); i++) {//初始化桌子
            desks.put(i, new DeskService(i, dataService, this));
        }
        //初始化各个档位的反分池
        for (int i = 0; i < configRoom.getBetArea().length; i++) {
            refund.put(i, new AtomicLong());
        }
        setTimerTask();//等待队列服务线程
    }

    public ConfigRoom getConfigRoom() {
        return configRoom;
    }

    /**
     * 进入房间
     * */
    public void joinRoom(PlayerService player) {
        if (waits.contains(player)) {
            return;
        }
        // 写入进房间记录
        boolean success = dataService.intoRoom(player.getPlayerId(), configRoom.getGameId(), configRoom.getRoomId());
        if (!success) {
            throw new ManagedException(-1, "进入房间失败！");
        }
        waits.add(player);//进入等待队列
    }

    public void begin(PlayerService playerService, int betIdx) {
        DeskService deskService = desks.get(playerService.getDeskId());
        if (deskService == null) {
            //未找到玩家
            throw new RuntimeException("玩家不存！");
        }
        synchronized (deskService.lock) {
            playerService.setLastUpdate(new Date());//最后操作时间
            deskService.begin(playerService, betIdx);
        }
    }

    public void select(PlayerService playerService, int index) {
        DeskService deskService = desks.get(playerService.getDeskId());
        if (deskService == null) {
            //未找到玩家
            throw new RuntimeException("玩家不存！");
        }
        synchronized (deskService.lock) {
            playerService.setLastUpdate(new Date());//最后操作时间
            deskService.select(playerService, index);
        }
    }

    /**
     * 离开房间
     * */
    public void leaveRoom(PlayerService playerService) {
        DeskService deskService = desks.get(playerService.getDeskId());
        if (deskService != null) {
            //离开桌子
            synchronized (deskService.lock) {
                deskService.leaveDesk(playerService.getPlayerId());
            }
        }
        //退出房间记录
        dataService.outRoom(playerService.getDeskPlayer().getPlayerId(), configRoom.getGameId(), configRoom.getRoomId());
    }

    private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    private void setTimerTask() {
        Runnable task = () -> {
            PlayerService playerService = null;
            try {
                playerService = waits.take();
                //是否满足进入条件
                if (!playerService.isOnline() || !canJoin(playerService)) {
                    throw new ManagedException(-1);
                }
                try {
                    //找一张空闲的桌子
                    Optional<DeskService> optional = getDesk();
                    //该场已无空位
                    if (!optional.isPresent()) {
                        playerService.sendMessage(SocketEnum.infoI, TishiManager.Tishi_T_No_Seat);
                        throw new ManagedException(-1);
                    }
                    DeskService deskService = optional.get();
                    synchronized (deskService.lock) {
                        InitDesk initDesk = deskService.joinDesk(playerService);//进入桌子
                        playerService.sendMessage(SocketEnum.initDeskI, JsonUtils.object2String(initDesk));
                        deskService.removeStatus(Status.IDEL);//移除空闲状态
                    }
                    // 写进入桌子记录
                    dataService.intoDesk(playerService.getPlayerId(), configRoom.getGameId(),
                            configRoom.getRoomId(), deskService.getNum(), 0);
                } catch (ManagedException e) {
                    logger.error("玩家进入桌子失败！", new Object[]{e});
                    gameService.removePlayer(playerService.getPlayerId());
                } catch (Exception e) {
                    gameService.exit(playerService);
                    logger.error("玩家进入桌子出错啦！", new Object[]{e});
                    playerService.sendMessage(SocketEnum.infoI, TishiManager.Tishi_T_JoinDesk_Fail);
                }
            } catch (Exception e) {
                logger.error("玩家等待队列发生未知错误！", e);
            }
        };
        scheduler.scheduleWithFixedDelay(task, 0, 200, TimeUnit.MILLISECONDS);
    }

    public void rebeat(long rebeat) {
        this.rebeat.addAndGet(rebeat);
    }

    public Optional<DeskService> getDesk() {
        return desks.values().stream().filter(ds -> ds.isIdle()).findFirst();
    }

    /**
     * 判断能否进入游戏
     * */
    private boolean canJoin(PlayerService playerService) throws Exception {
        BasePlayer player = playerService.getPlayer();
        //先获取并更新玩家背包
        dataService.getBagInfo(player);
        //金币入场限制检查
        Long[] limits = configRoom.getLimitMoney();
        long money = player.getMondey(PropType.GOLD);
        if (money < limits[0]) {
            playerService.sendMessage(SocketEnum.infoI, TishiManager.Tishi_T_MoneyTooLow);
            return false;
        }
        if (money >= limits[1]) {
            playerService.sendMessage(SocketEnum.infoI, TishiManager.Tishi_T_MoneyTooHigh);
            return false;
        }
        return true;
    }

    public AtomicLong getRefund(int betIndex) {
        return refund.get(betIndex);
    }

    public long gerDraw() {
        return rebeat.get();
    }

    public GameService getGameService() {
        return gameService;
    }
}
